package com.ideaaedi.springcloud.jd.yourbiz.config;

import com.ideaaedi.springcloud.jd.commonds.constant.BaseConstant;
import com.ideaaedi.springcloud.jd.commonspring.config.Knife4jConfig;
import com.ideaaedi.springcloud.jd.commonspring.oauth2.CustomAccessDeniedHandler;
import com.ideaaedi.springcloud.jd.commonspring.oauth2.CustomAuthExceptionEntryPoint;
import com.ideaaedi.springcloud.jd.commonspring.oauth2.CustomOAuth2AuthenticationManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler;
import org.springframework.security.oauth2.provider.token.TokenStore;

import javax.annotation.Resource;
import java.util.Arrays;

/**
 * spring-security-auth2资源服务器配置 <br />
 * <ul>
 *     注解说明：
 *     <li>EnableResourceServer - 标识当前服务器为auth2资源服务器</li>
 *     </li>
 * </ul>
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2021.0.1.A
 */
@RefreshScope
@Configuration
@EnableResourceServer
@SuppressWarnings("deprecation")
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class YourBizResourceConfig extends ResourceServerConfigurerAdapter {
    
    @Resource
    TokenStore tokenStore;
    
    @Resource
    OAuth2WebSecurityExpressionHandler oAuth2WebSecurityExpressionHandler;
    
    @Value("#{'${oauth2.permit-all-urls:/favicon.ico,/error}'.split(',')}")
    private String[]  permitAllUrls;
    
    @Value("#{'${oauth2.auth-urls:/**}'.split(',')}")
    private String[] authUrls;
    
    /**
     * oauth2资源服务配置
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(BaseConstant.SYSTEM_INNER_RESOURCE_ID)
                .authenticationManager(new CustomOAuth2AuthenticationManager(Arrays.asList(permitAllUrls)))
                .authenticationEntryPoint(new CustomAuthExceptionEntryPoint())
                .accessDeniedHandler(new CustomAccessDeniedHandler())
                // 设置spel处理器
                .expressionHandler(oAuth2WebSecurityExpressionHandler)
                .tokenStore(tokenStore)
        ;
    }
    
    /**
     * 安全 & 认证鉴权配置
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http    // ------------- session管理 -------------
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                // ------------- 认证鉴权配置 -------------
                .and()
                .authorizeRequests()
                // 浏览器进行跨域请求前会先进行一次options请求，设置允许OPTIONS请求
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                // 放行一些无需认证鉴权的地址
                .antMatchers(permitAllUrls).permitAll()
                .antMatchers(Knife4jConfig.RESOURCE_URLS).permitAll()
                
                .antMatchers(authUrls).access("@yourBizAccessDecisor.hasPermission(request, authentication)")
                // 请求资源服务器里面的任何资源，都需要认证
                .anyRequest().authenticated()
                // ------------- 其它配置 -------------
                .and()
                // (因为不使用session)禁用csrf
                .csrf().disable()
                // 防止iframe 造成跨域
                .headers().frameOptions().disable()
        ;
    }
}